package gui;
import java.awt.BorderLayout;
import java.awt.Container;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError;

import net.GameClient;
import net.GameServer;

import logic.OpenGame;
import logic.SaveGame;
import logic.Team;
import messages.CheckGameState;
import messages.ConnectTo;
import messages.DisconnectFromServer;
import messages.DisconnectServer;
import messages.GameEventListener;
import messages.ChangeTurn;
import messages.GameMessage;
import messages.MatchWinner;
import messages.MovePiece;
import messages.NewGame;
import messages.PlayerDisconnected;
import messages.QuitGame;
import messages.ResetMatch;
import messages.ConnectionEstablished;
import messages.RotatePiece;
import messages.SetBoardState;
import messages.StartGameServer;
import messages.StopGameServer;
import messages.UmountObelisk;


public class Gui implements GameEventListener{
	private Container mainComponent;
	private BoardGUI board;
	private GameMenu menu;
	private JFrame gameFrame;
	private String configurationFile;
	
	private StatusBar playerTurn;
	private StatusBar scoreBar;
	private StatusBar connectionStatus;
	private int redPlayerScore = 0;
	private int grayPlayerScore = 0;
	private GameServer server;
	private GameClient client;
	private boolean serving = false;
	
	private boolean connected = false;
	private Team localPlayerColor = Team.NOTEAM;
	
	public Gui(String configurationFile){
		this.configurationFile = configurationFile;
		gameFrame = new JFrame("FreeKhet");
		gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		board = new BoardGUI(configurationFile, this);
		
		menu = new GameMenu(this);
		menu.setVisible(true);
		
		setScore();
		setConnectionStatus();
		
		mainComponent = new Container();
		
		addComponentsToMainPane(mainComponent);
		
		gameFrame.setJMenuBar(menu);
		gameFrame.add(mainComponent);
		gameFrame.pack();
		gameFrame.setVisible(true);

		Runtime.getRuntime().addShutdownHook(shutDownAction);
	}
	
	public void resetGame(){
		board.resetMatch(configurationFile);
	}

	@Override
	public boolean gameEvent(GameMessage message) {
		if(message instanceof ChangeTurn){
			if(playerTurn==null)		
			playerTurn = new StatusBar();
			playerTurn.setText(message.getMessage());
			return true;
		}
		if(message instanceof ResetMatch){
			board.resetMatch(configurationFile);
			return true;
		}
		if(message instanceof NewGame){
			board.resetMatch(configurationFile);
			redPlayerScore = 0;
			grayPlayerScore = 0;
			setScore();
			return true;
		}
		if(message instanceof MatchWinner){
			MatchWinner messageWin = (MatchWinner) message;
			switch (messageWin.getWinner()) {
			case RED:
				redPlayerScore++;
				break;
			case GRAY:
				grayPlayerScore++;
				break;
			}
			setScore();
			Thread newThread = new Thread(new Runnable() {
				
				@Override
				public void run() {
					try {
						Thread.sleep(3000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					board.resetMatch(configurationFile);
					
				}
			});
			newThread.start();
			return true;
		}
		if(message instanceof ConnectTo){
			/*TODO
			 * do a connection to the server
			 */
			ConnectTo con = (ConnectTo) message;
			System.out.println("Trying to connect "+con.getServer()+":"+con.getPort());
			try {
				client = new GameClient(con.getServer(), con.getPort(), board ,this);
				connected = true;
				menu.clientConnected();
				setConnectionStatus();
				return true;
			} catch (UnknownHostException e) {
				System.err.println("Host "+con.getServer()+" unknown");
			} catch (SocketException e) {
				e.printStackTrace();
			} catch (IOException e) {
				/*
				 * Connection timeout
				 */
				System.err.println("No connection to "+con.getServer());
			}
		}
		if(message instanceof DisconnectServer){
			client.closeConnection();
			board.resetMatch(configurationFile);
			redPlayerScore = 0;
			grayPlayerScore = 0;
			setScore();
			board.unsetLocalPlayer();
			menu.clientDisconnected();
			setConnectionStatus();
			return true;
		}
		if(message instanceof QuitGame){
			if(connected&&serving){
				server.stopServer();
			}else if(connected){
				client.closeConnection();
			}
			gameFrame.dispose();
			return true;
		}
		if(message instanceof DisconnectFromServer){
			client.closeConnection();
			connected = false;
			menu.clientDisconnected();
			setConnectionStatus();
			return true;
		}
		if(message instanceof SaveGame){
			try {
				PrintWriter out = new PrintWriter(message.getMessage());
				out.println(board.getGameStateXML());
				out.close();
				return true;
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (ParserConfigurationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (TransformerException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
		if(message instanceof OpenGame){
			board.resetMatch(message.getMessage());
			redPlayerScore = 0;
			grayPlayerScore = 0;
			return true;
		}
		if(message instanceof StartGameServer){
			try {
				server = new GameServer(board, 5491, Team.RED);
				server.registerGameListener(this);
				serving = true;
				menu.startServer();
				setConnectionStatus();
				return true;
			} catch (SocketException e) {
				e.printStackTrace();
			}
		}
		if(message instanceof StopGameServer){
			server.stopServer();
			board.unsetLocalPlayer();
			serving = false;
			menu.stopServer();
			setConnectionStatus();
			return true;
		}
		if(message instanceof CheckGameState){
			try {
				String thisGameState = board.getGameStateXML();
				MessageDigest md5 = MessageDigest.getInstance("md5");
				byte[] hash = md5.digest(thisGameState.getBytes());
				String stateBoardMsg = "";
				for(int j=0;j<hash.length;j++){
					stateBoardMsg += Integer.toHexString(0xFF&hash[j]);
				}
				if(stateBoardMsg.equals(((CheckGameState)message).getMessage()))
						return true;
			} catch (ParserConfigurationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (TransformerException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchAlgorithmException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
		if(message instanceof SetBoardState){
			SetBoardState thisStateMsg = (SetBoardState) message;
			if(thisStateMsg.getConfiguration()==null)
				return false;
			board.resetMatch(thisStateMsg.getConfiguration());
			return true;
		}
		if(message instanceof ConnectionEstablished){
			ConnectionEstablished conn = (ConnectionEstablished)message;
			localPlayerColor = conn.getLocalColor();
			connected = true;
			board.setLocalPlayer(localPlayerColor);
			System.out.println(conn.getMessage());
			setConnectionStatus();
			return true;
		}
		if(message instanceof PlayerDisconnected){
			if(!serving)
				menu.clientDisconnected();
			connected = false;
			localPlayerColor = Team.NOTEAM;
			board.unsetLocalPlayer();
			System.out.println(message.getMessage());
			setConnectionStatus();
			return true;
		}
		if(message instanceof MovePiece){
			MovePiece msg = (MovePiece)message;
			if(serving&&connected&&msg.getPlayerTeam()==localPlayerColor){
				try {
					server.sendMsgToClient(msg.getXMLMessage());
					return true;
				} catch (TransformerFactoryConfigurationError e) {
					e.printStackTrace();
				} catch (TransformerException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}else if(connected&&msg.getPlayerTeam()==localPlayerColor){
				try {
					client.sendMsgToServer(msg.getXMLMessage());
					return true;
				} catch (TransformerFactoryConfigurationError e) {
					e.printStackTrace();
				} catch (TransformerException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}else{
				return board.movePiece(msg.getX1(), msg.getY1(), msg.getX2(), msg.getY2(), msg.getPlayerTeam());
			}
		}
		if(message instanceof RotatePiece){
			RotatePiece rotateMsg = (RotatePiece)message;
			if(serving&&connected&&rotateMsg.getTeam()==localPlayerColor){
				try {
					server.sendMsgToClient(rotateMsg.getXMLMessage());
				} catch (TransformerFactoryConfigurationError e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (TransformerException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}else if(connected&&rotateMsg.getTeam()==localPlayerColor){
				try {
					client.sendMsgToServer(rotateMsg.getXMLMessage());
				} catch (TransformerFactoryConfigurationError e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (TransformerException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}else{
				return rotateMsg.rotatePiece(board);
			}
		}
		if(message instanceof UmountObelisk){
			UmountObelisk msg = (UmountObelisk)message;
			if(serving&&connected&&msg.getPlayerTeam()==localPlayerColor){
				try {
					server.sendMsgToClient(msg.getXMLMessage());
					return true;
				} catch (TransformerFactoryConfigurationError e) {
					e.printStackTrace();
				} catch (TransformerException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}else if(connected&&msg.getPlayerTeam()==localPlayerColor){
				try {
					client.sendMsgToServer(msg.getXMLMessage());
					return true;
				} catch (TransformerFactoryConfigurationError e) {
					e.printStackTrace();
				} catch (TransformerException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}else{
				return board.umountObelisk(msg.getX1(), msg.getY1(), msg.getX2(), msg.getY2(), msg.getPlayerTeam());
			}
		}
		
		return false;
	}
	
	public void addComponentsToMainPane(Container pane){
		pane.setLayout(new BorderLayout());
		pane.add(board, BorderLayout.PAGE_START);
		
		JPanel bottomContainer = new JPanel(new BorderLayout());
		bottomContainer.add(playerTurn, BorderLayout.LINE_START);
		bottomContainer.add(scoreBar, BorderLayout.LINE_END);
		bottomContainer.add(connectionStatus, BorderLayout.PAGE_END);
		bottomContainer.setVisible(true);
		pane.add(bottomContainer, BorderLayout.PAGE_END);

	}
	
	private void setScore(){
		if(scoreBar==null)
			scoreBar = new StatusBar();
		scoreBar.setText("Red score: "+redPlayerScore+" Gray Score: "+grayPlayerScore);
	}

	private void setConnectionStatus(){
		if(connectionStatus==null)
			connectionStatus = new StatusBar();
		if(serving&&!connected){
			connectionStatus.setText("Waiting for connections");
		}else if(connected){
			/*
			 * TODO
			 * get information of the player
			 */
			connectionStatus.setText("Connected to "+"player"+"\n my color is "+localPlayerColor);
		}else{
			connectionStatus.setText("StandAlone");
		}
	}
	
	private Thread shutDownAction = new Thread(new Runnable() {
		
		@Override
		public void run() {
			if(connected&&serving){
				server.stopServer();
			}else if(connected){
				client.closeConnection();
			}
		}
	});

}
